#pragma once

#include "Region.h"

#include "Classifier.h"
#include "RedefinableElement.h"
#include "State.h"
#include "StateMachine.h"
#include "Transition.h"
#include "Vertex.h"

namespace uml {

Region::Region(const QString& umlType)
    : NamedElement(umlType) {}

void Region::fromJson(const QJsonObject& json, QStringList& errorList) {
    if (json.isEmpty()) {
        return;
    }

    NamedElement::fromJson(json, errorList);
    Namespace::fromJson(json, errorList);
    RedefinableElement::fromJson(json, errorList);
    extendedRegion_ = JsonSerializable::create<Region>(json.value("extendedRegion"), errorList);

    redefinitionContext_ = JsonSerializable::create<Classifier>(json.value("redefinitionContext"), errorList);

    state_ = JsonSerializable::create<State>(json.value("state"), errorList);

    stateMachine_ = JsonSerializable::create<StateMachine>(json.value("stateMachine"), errorList);

    JsonSerializable::fromJsonArray(subvertexs_, "subvertexs", json, errorList);
    JsonSerializable::fromJsonArray(transitions_, "transitions", json, errorList);
}

void Region::toJson(QJsonObject& json) {
    NamedElement::toJson(json);
    Namespace::toJson(json);
    RedefinableElement::toJson(json);
    if (extendedRegion_) {
        QJsonObject extendedRegion_json;
        extendedRegion_->toJson(extendedRegion_json);
        json.insert("extendedRegion", extendedRegion_json);
    }
    if (redefinitionContext_) {
        QJsonObject redefinitionContext_json;
        redefinitionContext_->toJson(redefinitionContext_json);
        json.insert("redefinitionContext", redefinitionContext_json);
    }
    if (state_) {
        QJsonObject state_json;
        state_->toJson(state_json);
        json.insert("state", state_json);
    }
    if (stateMachine_) {
        QJsonObject stateMachine_json;
        stateMachine_->toJson(stateMachine_json);
        json.insert("stateMachine", stateMachine_json);
    }
    JsonSerializable::toJsonArray(subvertexs_, "subvertexs", json);
    JsonSerializable::toJsonArray(transitions_, "transitions", json);
}

/// <summary>
/// belongsToPSM
    /// The operation belongsToPSM () checks if the Region belongs to a
    /// ProtocolStateMachine.
/// </summary>
    /// <code>
    /// result = (if  stateMachine <> null 
    /// then
    ///   stateMachine.oclIsKindOf(ProtocolStateMachine)
    /// else 
    ///   state <> null  implies  state.container.belongsToPSM()
    /// endif )
    /// </code>
bool Region::belongsToPSM() const
{
    return false;
}

/// <summary>
/// containingStateMachine
    /// The operation containingStateMachine() returns the StateMachine in which this
    /// Region is defined.
/// </summary>
    /// <code>
    /// result = (if stateMachine = null 
    /// then
    ///   state.containingStateMachine()
    /// else
    ///   stateMachine
    /// endif)
    /// </code>
shared_ptr<StateMachine> Region::containingStateMachine() const
{
    return nullptr;
}

/// <summary>
/// isConsistentWith
    /// The query isConsistentWith() specifies that a redefining Region is consistent
    /// with a redefined Region provided that the redefining Region is an extension of
    /// the Redefined region, i.e., its Vertices and Transitions conform to one of the
    /// following: (1) they are equal to corresponding elements of the redefined Region
    /// or, (2) they consistently redefine a State or Transition of the redefined
    /// region, or (3) they add new States or Transitions.
/// </summary>
    /// <code>
    /// result = (-- the following is merely a default body; it is expected that the specific form of this constraint will be specified by profiles
    /// true)
    /// </code>
    /// <code>
    /// redefiningElement.isRedefinitionContextValid(self)
    /// </code>
bool Region::isConsistentWith(shared_ptr<RedefinableElement> redefiningElement) const
{
    return false;
}

/// <summary>
/// isRedefinitionContextValid
    /// The query isRedefinitionContextValid() specifies whether the redefinition
    /// contexts of a Region are properly related to the redefinition contexts of the
    /// specified Region to allow this element to redefine the other. The containing
    /// StateMachine or State of a redefining Region must Redefine the containing
    /// StateMachine or State of the redefined Region.
/// </summary>
    /// <code>
    /// result = (if redefinedElement.oclIsKindOf(Region) then
    ///   let redefinedRegion : Region = redefinedElement.oclAsType(Region) in
    ///     if stateMachine->isEmpty() then
    ///     -- the Region is owned by a State
    ///       (state.redefinedState->notEmpty() and state.redefinedState.region->includes(redefinedRegion))
    ///     else -- the region is owned by a StateMachine
    ///       (stateMachine.extendedStateMachine->notEmpty() and
    ///         stateMachine.extendedStateMachine->exists(sm : StateMachine |
    ///           sm.region->includes(redefinedRegion)))
    ///     endif
    /// else
    ///   false
    /// endif)
    /// </code>
bool Region::isRedefinitionContextValid(shared_ptr<RedefinableElement> redefinedElement) const
{
    return false;
}

/// <summary>
/// extendedRegion getter
/// The region of which this region is an extension.
/// </summary>
shared_ptr<Region> Region::extendedRegion() const {
    return extendedRegion_;
}

/// <summary>
/// extendedRegion setter
/// The region of which this region is an extension.
/// </summary>
void Region::setExtendedRegion(shared_ptr<Region> value) {
    extendedRegion_ = value;
}

/// <summary>
/// redefinitionContext getter
/// References the Classifier in which context this element may be redefined.
/// </summary>
shared_ptr<Classifier> Region::redefinitionContext() const {
    return redefinitionContext_;
}

/// <summary>
/// redefinitionContext setter
/// References the Classifier in which context this element may be redefined.
/// </summary>
void Region::setRedefinitionContext(shared_ptr<Classifier> value) {
    redefinitionContext_ = value;
}

/// <summary>
/// state getter
/// The State that owns the Region. If a Region is owned by a State, then it cannot
/// also be owned by a StateMachine.
/// </summary>
shared_ptr<State> Region::state() const {
    return state_;
}

/// <summary>
/// state setter
/// The State that owns the Region. If a Region is owned by a State, then it cannot
/// also be owned by a StateMachine.
/// </summary>
void Region::setState(shared_ptr<State> value) {
    state_ = value;
}

/// <summary>
/// stateMachine getter
/// The StateMachine that owns the Region. If a Region is owned by a StateMachine,
/// then it cannot also be owned by a State.
/// </summary>
shared_ptr<StateMachine> Region::stateMachine() const {
    return stateMachine_;
}

/// <summary>
/// stateMachine setter
/// The StateMachine that owns the Region. If a Region is owned by a StateMachine,
/// then it cannot also be owned by a State.
/// </summary>
void Region::setStateMachine(shared_ptr<StateMachine> value) {
    stateMachine_ = value;
}

/// <summary>
/// subvertexs getter
/// The set of Vertices that are owned by this Region.
/// </summary>
vector<shared_ptr<Vertex>>& Region::subvertexs() {
    return subvertexs_;
}

/// <summary>
/// transitions getter
/// The set of Transitions owned by the Region.
/// </summary>
vector<shared_ptr<Transition>>& Region::transitions() {
    return transitions_;
}

} // namespace uml
